﻿/// /////////////////////////////////////////////
// 单元名 : unitUpdateClientObj
// 作者 : Hawk
// 单元描述 : 更新客户端对象
// 日期 : 2023-09-15
/// /////////////////////////////////////////////

{$IFDEF FPC}
{$H+}
{$MODE DELPHI}
{$ENDIF}
unit unitUpdateClientObj;

interface

uses
{$IFDEF MSWINDOWS}Windows, {$ENDIF} Messages, unitCommonDataStruct, unitObjBase, unitClientUpdateObj, unitCommonTypes,
  unitBlockMsgClient, unitSvrMngCommon, unitMsgObj, unitClientUpdateMsgDefine, unitNetCommon, unitAAACommon;

type
  TOnUpdateProgress = procedure(Sender: TObject; Progress, Err: LongWord) of object;

  TUpdateClientObj = class(TObjBase)
  private
    FOnUpdateProgress: TOnUpdateProgress;
    FInstanceGUID: TID_OBJ;
    FProductID: TID_OBJ;
    FWnd: HWND;
    FMsgClient: TBlockMsgClient;
    FUpdateablePrj: TUpdateableProject;
    FInstallPath, FClientName: string;
    FCommID: LongWord;

    procedure StartUpdate;
    procedure ProgressNotify(Progress: LongWord; Err: ERRCODE);
    procedure SetClientName(const Value: string);
    procedure SetProductID(const Value: TID_OBJ);
    procedure ExecAfterUpdate(Path: string);

    procedure SetMsgClient(const Value: TBlockMsgClient);
    procedure ProcRecvMsg(Sender: TObject; msg: TMsg);
    procedure SendMsgEntry(m: TMsg);
    procedure ConnCreate(Sender: TObject);
    procedure ConnBreak(Sender: TObject);

    procedure ProcessAccessDenied(msg: TMsg);
    procedure ProcRegSucc(msg: TMsg);
    procedure ProcRecvClientVerInfo(msg: TMsg);
    procedure ProcProjectNotFound(msg: TMsg);
    procedure ProcRecvFileContent(msg: TMsg);
  protected
    function DoProcessMsg(msg: TMsg): Boolean; override;
  public
    property Wnd: HWND read FWnd write FWnd;
    property ClientName: string read FClientName write SetClientName;
    property ProductID: TID_OBJ read FProductID write SetProductID;
    property ClientGUID: TID_OBJ read FInstanceGUID write FInstanceGUID;
    property MSgClient: TBlockMsgClient read FMsgClient write SetMsgClient;
    property OnUpdateProgress: TOnUpdateProgress read FOnUpdateProgress write FOnUpdateProgress;
  end;

const
  S_DLCNTX64 = 'I-D83ADC2E189FDBA5AABBFB9521F7C6';
  S_DLCNTX86 = 'I-2ECB6F570C679AA38F3FDE11BACC54';

implementation

uses SysUtils, unitMisc, unitObjPool, unitCommonIntf, unitErrCodeDef;

{ TUpdateClientObj }

procedure TUpdateClientObj.ConnBreak(Sender: TObject);
begin
  FMsgClient.OnConnected := nil;
  FMsgClient.OnBreak := nil;
  FMsgClient.OnRecvMsg := nil;
  FMsgClient := nil;
  ProgressNotify(UPDATEPROGRESS_ACCESSDENYED, ERRCODE_OVERTIME);
end;

procedure TUpdateClientObj.ConnCreate(Sender: TObject);
var
  m: TMsg;
  dd: TDeviceDesc;
begin
  m := GetAMsg;
  m.TargetObjID := MakeObjID(HOSTID_ROOT, SVRMODULE_ID);
  m.MsgID := MESID_NET_REGHOSTID;
  dd := ObjPool.GetObj(TDeviceDesc, Self);
  if dd <> nil then
  begin
    dd.ProductID := FProductID;
    dd.DevName := DevName;
    dd.DevType := dtWindows;
    dd.SelfSessionID := FInstanceGUID;
    m.AddObj(dd);
  end;
  SendMsgEntry(m);
end;

function TUpdateClientObj.DoProcessMsg(msg: TMsg): Boolean;
begin
  Result := True;
  Writeln('Recv msg from: ', msg.SourceObjID.ToString, '  ', IntToHex(msg.MsgID, 8));
  case msg.MsgID of
    MSGID_ALL_START:
      StartUpdate;
    MESID_ALL_TERMINATE:
      ProgressNotify(UPDATEPROGRESS_UPDATECOMPLETE, ERRCODE_NOERROR);
    MESID_ACCESS_DENIED:
      ProcessAccessDenied(msg);
    MESID_NET_HOSTIDSUCC:
      ProcRegSucc(msg);
    MESID_NET_HOSTIDREGED:
      ProgressNotify(UPDATEPROGRESS_ACCESSDENYED, ERRCODE_OBJEXIST);
    MESID_SVRMNG_SENDCLIENTVERINFO:
      ProcRecvClientVerInfo(msg);
    MESID_SVRMNG_CLIENTPROJECTNOTFOUND:
      ProcProjectNotFound(msg);
    MESID_SVRMNG_SENDFILECONTENT:
      ProcRecvFileContent(msg);
  end;
end;

procedure TUpdateClientObj.ExecAfterUpdate(Path: string);
const
  S_SETNAME = 'set.bat';
var
  t: System.Text;
  sname: string;
  spath, line: AnsiString;
begin
  sname := TempPath + S_SETNAME;
  spath := AnsiString(Path);
  if (spath <> '') and (spath[Length(spath)] = PathDelim) then
    Delete(spath, Length(spath), 1);
  AssignFile(t, sname);
  // DbgPrint(L_DBGLEVEL_DEBUG, 'Update comp, ready %s %s', [sname, spath]);
  ReWrite(t);
  line := 'icacls "' + spath + '" /T /Q /C /RESET';
  Writeln(t, line);
  line := '"' + spath + PathDelim + 'service.exe" install';
  Writeln(t, line);
  line := 'reg add HKLM\SOFTWARE\Microsoft\Windows\CurrentVersion\Run /f /v DlCnt /t REG_SZ /d "' + spath + PathDelim +
    'DLCnt.exe"';
  Writeln(t, line);
  line := 'start net start HawkServer';
  Writeln(t, line);
  CloseFile(t);
  spath := AnsiString(sname);
  DbgPrint('Script writed. execute it');
  if WinExec(PAnsiChar(spath), SW_HIDE) <> 0 then
    DbgPrintSysError('Script exec error!');
end;

procedure TUpdateClientObj.ProcessAccessDenied(msg: TMsg);
begin
  ProgressNotify(UPDATEPROGRESS_ACCESSDENYED, msg.Error);
end;

procedure TUpdateClientObj.ProcProjectNotFound(msg: TMsg);
begin
  ProgressNotify(UPDATEPROGRESS_PROJECTNOTFOUND, ERRCODE_PROJECTNOTEXIST);
end;

procedure TUpdateClientObj.ProcRecvClientVerInfo(msg: TMsg);
var
  prj: TUpdateableProject;
  m: TMsg;
  fo: TUpdateFileObj;
  s: string;
begin
  prj := msg.Param(TUpdateableProject);
  if prj <> nil then
  begin
    FUpdateablePrj := prj;
    msg.DelObj(prj);
    prj.CheckUpdateableObjs;
    if prj.Count > 0 then
    begin
      s := prj.DeployPath;
{$IFDEF DEBUG}
      Writeln('创建部署目录：', s, '需要下载的文件数量：', IntToStr(prj.Count));
{$ENDIF}
      CreatePath(s);
      ProgressNotify(UPDATEPROGRESS_STARTDOWNLOADFILE, ERRCODE_NOERROR);
      m := GetAMsg;
      m.TargetObjID := ROOTID_SVRMNGMODULE;
      m.MsgID := MESID_SVRMNG_GETUPDATEFILE;
      fo := TUpdateFileObj(prj.Item[0]);
      while fo <> nil do
      begin
{$IFDEF DEBUG}
        Writeln('需要下载的文件ID：', fo.FileID.ToString);
{$ENDIF}
        m.AddIDObj(fo.FileID, '', nil, True);
        fo := TUpdateFileObj(fo.Next);
      end;
      SendMsgEntry(m);
    end
    else
      ProgressNotify(UPDATEPROGRESS_UPDATECOMPLETE, ERRCODE_NOERROR);
  end;
end;

procedure TUpdateClientObj.ProcRecvFileContent(msg: TMsg);
var
  fc: TFileContent;
  s: string;
begin
  ProgressNotify(UPDATEPROGRESS_REPLACELOCALFILE, ERRCODE_NOERROR);
  if FUpdateablePrj <> nil then
  begin
    while msg.Count > 0 do
    begin
      fc := msg.Param(TFileContent);
      if fc <> nil then
      begin
        if fc.FileName <> '' then
          FUpdateablePrj.UpdateFileObj(fc.ObjID, fc.FileName);
        ObjPool.ReturnObj(fc, Self);
      end
      else
        Break;
    end;
    s := FProductID.ToString;
    if (s = S_DLCNTX64) or (s = S_DLCNTX86) then
      ExecAfterUpdate(FUpdateablePrj.DeployPath);
  end;
  ProgressNotify(UPDATEPROGRESS_UPDATECOMPLETE, ERRCODE_NOERROR);
end;

procedure TUpdateClientObj.ProcRecvMsg(Sender: TObject; msg: TMsg);
var
  m: TMsg;
begin
  if msg.CommID <> 0 then
  begin
    m := GetAMsg(msg);
    m.MsgID := MESID_NET_CONFIRMCOMMID;
    m.CommID := msg.CommID;
    FMsgClient.SendMsg(m);
  end;
  DoProcessMsg(msg);
  ObjPool.ReturnObj(msg, Self);
end;

procedure TUpdateClientObj.ProcRegSucc(msg: TMsg);
var
  m: TMsg;
  prj: TUpdateableProject;
begin
  FCommID := $1023;
{$IFDEF DEBUG}
  Writeln('更新的项目 ID：', FProductID.ToString);
{$ENDIF}
  m := GetAMsg;
  m.TargetObjID := ROOTID_SVRMNGMODULE;
  m.MsgID := MESID_SVRMNG_GETUPDATEVERINFO;
  prj := ObjPool.GetObj(TUpdateableProject, Self);
  prj.ObjID := FProductID;
  m.AddObj(prj);
  SendMsgEntry(m);
  ProgressNotify(UPDATEPROGRESS_FETCHUPDATEINFO, ERRCODE_NOERROR);
end;

procedure TUpdateClientObj.ProgressNotify(Progress: LongWord; Err: ERRCODE);
begin
  if FWnd <> 0 then
    PostMessage(FWnd, WM_NOTIFYMESSAGE, Progress, Err)
  else if Assigned(FOnUpdateProgress) then
    FOnUpdateProgress(Self, Progress, Err);
end;

procedure TUpdateClientObj.SendMsgEntry(m: TMsg);
begin
  if FMsgClient <> nil then
  begin
    Inc(FCommID);
    m.CommID := FCommID;
    FMsgClient.SendMsg(m);
  end
  else
    CommonIntf.SendMsgProc(Self, m);
end;

procedure TUpdateClientObj.SetClientName(const Value: string);
begin
  if FClientName <> Value then
  begin
    FClientName := Value;
    FProductID.Empty;
  end;
end;

procedure TUpdateClientObj.SetMsgClient(const Value: TBlockMsgClient);
begin
  if Value <> nil then
  begin
    FMsgClient := Value;
    FMsgClient.OnConnected := ConnCreate;
    FMsgClient.OnBreak := ConnBreak;
    FMsgClient.OnRecvMsg := ProcRecvMsg;
  end;
end;

procedure TUpdateClientObj.SetProductID(const Value: TID_OBJ);
begin
  if FProductID <> Value then
  begin
    FProductID := Value;
    FClientName := '';
  end;
end;

procedure TUpdateClientObj.StartUpdate;
var
  a: array [0 .. MAX_PATH] of TCHAR;
begin
  ExpandEnvironmentStrings('%ProgramW6432%', a, SizeOf(a));
  FInstallPath := a;
  FInstallPath := FInstallPath + PathDelim;
end;

end.
